﻿using System.Linq;
using System.Text;
using Omu.Encrypto;
using Uber.Core.Model;
using System;
using System.Configuration;
using Uber.Core.Repository;
using Uber.Core.Service;

namespace Uber.Service
{
    public class Accounts : IAccounts
    {
        private readonly IRepo<Account> accountRepo;
        private readonly IRepo<AweOid> aweOidRepo;
        private readonly IRepo<Recover> recoverRepo; 
        private readonly IMailSender mailSender;
        private readonly IHasher hasher;

        public Accounts(IRepo<Account> accountRepo, IMailSender mailSender, IRepo<AweOid> aweOidRepo, IHasher hasher, IRepo<Recover> recoverRepo)
        {
            this.accountRepo = accountRepo;
            this.aweOidRepo = aweOidRepo;
            this.hasher = hasher;
            this.recoverRepo = recoverRepo;
            this.mailSender = mailSender;
            hasher.SaltSize = 10;
        }

        public Account Get(long id)
        {
            return accountRepo.Get(id);
        }

        public void SaveMailSettings(bool mailOnAnswer, bool mailOnComment, long userId)
        {
            var o = accountRepo.Get(userId);
            o.MailOnAnswer = mailOnAnswer;
            o.MailOnComment = mailOnComment;
            accountRepo.Save();
        }

        public void EditProfile(long id, string aemail, string displayName)
        {
            var a = Get(id);
            a.Aemail = aemail;
            a.DisplayName = displayName;
            accountRepo.Save();
        }

        public long OpenIdLogIn(string oid, string email, string name)
        {
            var account = accountRepo.Where(o => o.Oid == oid).SingleOrDefault();
            
            if (account != null)
                return account.Id;
            
            var newa = new Account {Oid = oid, DisplayName = name, Email = email, CreationDate = DateTime.Now};
            accountRepo.Add(newa);
            accountRepo.Save();
            return newa.Id;
        }

        public bool IsPassRight(long id, string pass)
        {
            var ac = accountRepo.Get(id);
            var a = aweOidRepo.Get(Convert.ToInt64(ac.Oid));
            return hasher.CompareStringToHash(pass, a.Password);
        }

        public long LogIn(string userName, string password)
        {
            var a = aweOidRepo.Where(o => o.Email == userName).SingleOrDefault();
            if (a == null || !hasher.CompareStringToHash(password, a.Password)) return 0;

            if (a.Code != 0) return -1;

            var oid = a.Id.ToString();
            var oa = accountRepo.Where(o => o.IsAweOid && o.Oid == oid).SingleOrDefault();
            return oa == null ? 0 : oa.Id;
        }

        public bool RequestRecover(string email)
        {
            var o = new Recover {Email = email, Time = DateTime.Now, Code = MakeCode()};
            recoverRepo.Add(o);
            recoverRepo.Save();
            mailSender.SendEmail("omu@omu.com",
                    o.Email,
                    "instructions to reset password",
                    string.Format("to reset your password go to {0}/Account/ResetPassword?code={1}&email={2}", ConfigurationManager.AppSettings["host"], o.Code, o.Email));
            return true;
        }

        public bool IsRecoverValid(long code, string email)
        {
            return recoverRepo.Where(o => o.Code == code && o.Email == email).Count() > 0;
        }

        public bool Create(string email, string pass, string displayName)
        {
            email = email.Trim().ToLower();
            if (EmailExists(email)) throw new UberEx("this email is already in use");
            var code = MakeCode();
            var o = new AweOid
                        {
                            Email = email,
                            Code = code,
                            Password = hasher.Encrypt(pass)
                        };
            aweOidRepo.Add(o);
            aweOidRepo.Save();
            
            var a = new Account
                        {
                            Email = email,
                            DisplayName = displayName,
                            Oid = o.Id.ToString(),
                            IsAweOid = true,
                            CreationDate = DateTime.Now
                        };
            accountRepo.Add(a);
            accountRepo.Save();

            SendActivationCode(code, email);
            return true;
        }

        public bool EmailExists(string email)
        {
            return aweOidRepo.Where(o => o.Email == email).Count() > 0;
        }

        public bool Activate(long code, string email)
        {
            var r = aweOidRepo.Where(o => o.Code == code && o.Email == email).SingleOrDefault();
            if (r == null) return false;
            r.Code = 0;
            recoverRepo.Save();
            return true;
        }

        public bool ChangePass(long id, string pass)
        {
            var ac = accountRepo.Get(id);

            var a = aweOidRepo.Get(Convert.ToInt64(ac.Oid));
            if (a == null) return false;
            a.Password = hasher.Encrypt(pass);
            aweOidRepo.Save();
            return true;
        }

        public void SendActivationCode(long code, string email)
        {
            var content = new StringBuilder();
            content.AppendLine("You're almost done!");
            content.AppendLine(string.Format("to complete your registration go here: {0}/Success/ActivateAccount?code={1}&email={2}",
                ConfigurationManager.AppSettings["host"],code,email));
            mailSender.SendEmail("noreply@awesome.com", email, "Complete Registration on awesome.com", content.ToString());
        }

        public long MakeCode()
        {
            var bytes = new byte[sizeof(long)];
            new Random().NextBytes(bytes);
            // strip out the sign bit
            bytes[7] = (byte)(bytes[7] & 0x7f);
            return BitConverter.ToInt64(bytes, 0);
        }


        public string ResendActivationCode(string email)
        {
            var a = aweOidRepo.Where(o => o.Email == email).SingleOrDefault();
            if(a == null) return "This account doesn't exists";
            
            if (a.Code == 0) return "Your account was already activated";

            SendActivationCode(a.Code, a.Email);
            return null;
        }

        public bool ResetPassword(string email, long code, string pass)
        {
            var rec = recoverRepo.Where(o => o.Email == email && o.Code == code).SingleOrDefault();
            if (rec == null) return false;

            recoverRepo.Delete(rec.Id);
            recoverRepo.Save();
            
            var a = aweOidRepo.Where(o => o.Email == email).Single();
            a.Password = hasher.Encrypt(pass);
            aweOidRepo.Save();

            //mailSender.SendEmail("omu@omu.com", a.Email, "password reset", string.Format("your new password is: {0}", pass));
            return true;
        }
    }
}